package Proofs;


import ArithmeticLibrary.Elements.ElementField;
import ArithmeticLibrary.Elements.ElementGroup;
import ArithmeticLibrary.Elements.ElementProductField;
import ArithmeticLibrary.Elements.ElementProductGroup;
import ArithmeticLibrary.Sets.Group;
import ArithmeticLibrary.Sets.ProductGroup;

/**
 * This class contains functions of the El-Gamal Cryptosystem.
 * @author Hadar Miller and Or Stern
 *
 */
public class ProofsUtil {
	
	/**
	 * Encrypt the message m using randomness s and returns ciphertext.
	 * @param pk El Gamal public key.
	 * @param m Plaintexts.
	 * @param s Random exponent.
	 * @return Ciphertexts.
	 * @throws Exception the calculation in the encryption process failed.
	 */
	public static ElementProductGroup Enc(ElementProductGroup pk, ElementProductGroup m, ElementProductField s) throws Exception {

		ElementProductGroup enc;
		
		try {
			ElementGroup g = pk.get(0);
			
			ElementGroup y = pk.get(1);
			
			Group [] groups = new Group[2];
			Group Mw = m.getGroup();
			groups[0] = Mw;		
			groups[1] = Mw;
			
			ProductGroup Cw = new ProductGroup(groups);
			
			int w = ((ProductGroup) Mw).getNumberOfElements();
			
			enc = new ElementProductGroup(Cw);
			ElementProductGroup enc0 = new ElementProductGroup((ProductGroup) Mw);
			ElementProductGroup enc1 = new ElementProductGroup((ProductGroup) Mw);
			
			for (int i = 0; i < w; i++) {
				enc0.set(i, g.power(s.get(i).getElementValue()).multInverse());
				enc1.set(i, m.get(i).multiply(y.power(s.get(i).getElementValue()).multInverse()));
			}
			enc.set(0, enc0);
			enc.set(1, enc1);
		} catch (Exception e){
			throw new Exception("Encription Failed\n" + e.getMessage());
		}
		
		return enc;
	
	}
	
	/**
	 * Receives as an input a ciphertext and return a partial decryption factor.
	 * @param uv Ciphertexts.
	 * @param x Secret key.
	 * @return Plaintexts.
	 * @throws Exception the calculation in the decryption process failed.
	 */
	public static ElementProductGroup PDec(ElementProductGroup uv, ElementField x) throws Exception {
		
		ElementProductGroup dec;
		try {
			ElementProductGroup u = (ElementProductGroup) uv.get(0);
			
			ElementProductGroup v = (ElementProductGroup) uv.get(1);
			
			
			ProductGroup Mw = (ProductGroup) u.getGroup();
			
			int w = Mw.getNumberOfElements();
			
			dec = new ElementProductGroup(Mw);
			
			ElementGroup uPowX;
			for (int i = 0; i < w; i++) {
				uPowX = u.get(i).power(x.getElementValue());
				dec.set(i, v.get(i).multiply(uPowX.multInverse()));
		
			}
		} catch (Exception e) {
			throw new Exception("PDecryption Failed\n" + e.getMessage());
		}
		
		return dec;
	
	}
	

	/**
	 * Receives a joint decryption factor (combined from all the decryption factors) such 
	 * that PDec_x (u,v)=f and returns vf = m.
	 * @param uv Ciphertexts.
	 * @param f Combined decryption factors.
	 * @return Plaintexts.
	 * @throws Exception the calculation in the decryption process failed.
	 */
	public static ElementProductGroup TDec(ElementProductGroup uv, ElementProductGroup f) throws Exception {
	
		ElementProductGroup dec;
		try {
		
			ElementProductGroup v = (ElementProductGroup) uv.get(1);
			
			dec = v.multiply(f);
		} catch (Exception e) {
			throw new Exception("TDecryption Failed\n" + e.getMessage());
		}
		
		return dec;
	
	}
}
